﻿using System;
using Codeflows;
using Codeflows.Dispatch;
using CodeflowsTests.Scenarios;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CodeflowsTests
{
    [TestClass]
    public class StateMachineTests
    {
    	[TestInitialize]
    	public void SetUp()
    	{
			CodeflowApplication.SetCodeflowTrackerFactory(Tracker.InMemory);	
    	}

        [TestMethod]
        public void When_running_a_codeflow_with_just_one_action_it_should_run_to_completion()
        {
            WhenCodeflow.Executed<CodeflowWithJustTheEntryPoint>(c => c.Start()).ItShouldRunToCompletion();
        }

        [TestMethod]
        public void When_running_a_codeflow_with_a_simple_jump_it_should_run_to_completion()
        {
            WhenCodeflow.Executed<SimpleJump>(c => c.Start()).ItShouldRunToCompletion();
        }

        [TestMethod]
        public void When_running_a_codeflow_with_a_jump_with_arguments_it_should_run_to_completion()
        {
            WhenCodeflow.Executed<JumpWithArguments>(c => c.Start()).ItShouldRunToCompletion();
        }

        [TestMethod]
        [ExpectedException(typeof (CodeflowException))]
        public void When_running_a_codeflow_with_an_invalid_jump_target_it_should_throw_an_exception()
        {
            WhenCodeflow.Executed<InvalidJumpTarget>(c => c.Start());
        }

        [TestMethod]
        [ExpectedException(typeof (CodeflowException))]
        public void When_codeflow_throws_an_unhandled_exception_it_should_throw_an_exception()
        {
            WhenCodeflow.Executed<CodeflowWithUnhandledException>(c => c.Start());
        }

        [TestMethod]
        public void When_codeflow_throws_an_unhandled_exception_inner_exception_should_contain_the_actual_exception()
        {
            Exception originalException = null;
            try
            {
                WhenCodeflow.Executed<CodeflowWithUnhandledException>(c => c.Start());
            }
            catch (CodeflowException ex)
            {
                originalException = ex.InnerException;
            }

            Assert.IsInstanceOfType(originalException, typeof (NotImplementedException));
        }

        [TestMethod]
        public void Codeflow_with_an_echo_should_work()
        {
            WhenCodeflow.Executed<Echo>(c => c.Start("hola")).ItShouldEndWithValue("hola");
        }

        [TestMethod]
        public void When_a_codeflow_is_paused_it_should_be_in_correct_state()
        {
            WhenCodeflow.Executed<Pause>(c => c.Start()).ItShouldBePaused();
        }

        [TestMethod]
        public void When_a_codeflow_is_paused_it_can_be_resumed()
        {
            WhenCodeflow.Executed<Pause>(c => c.Start()).ThenResume(c => c.Resume()).ItShouldRunToCompletion();
        }

        [TestMethod]
        [ExpectedException(typeof (CodeflowException))]
        public void When_attempting_to_resume_from_an_invalid_location_it_should_fail()
        {
            WhenCodeflow.Executed<Pause>(c => c.Start()).ThenResume(c => c.Finish());
        }

        [TestMethod]
        public void When_an_action_method_has_a_paramater_of_iactioncontext_it_should_receive_the_runtime_value()
        {
            WhenCodeflow.Executed<DispatchCountingCodeflow>(c => c.Start(RuntimeValues.ActionContext)).
                ItShouldEndWithValue(4);
        }

        [TestMethod]
        public void When_a_delayed_jump_is_returned_codeflow_state_should_be_correct()
        {
            var delay = TimeSpan.FromSeconds(10);
            WhenCodeflow.Executed<DelayedJump>(c => c.Start(10)).ItShouldBeAsleep();
        }

        [TestMethod]
        public void When_executing_an_async_codeflow_it_should_return_the_correct_result()
        {
            WhenCodeflow.AsynchronouslyExecuted<AsyncCodeflow>(c => c.Start()).AfterAllPendings().ItShouldEndWithValue(
                10);
        }

        [TestMethod]
        public void When_an_async_codeflow_throws_an_unhandled_exception_it_should_be_in_the_correct_state()
        {
            WhenCodeflow.AsynchronouslyExecuted<AsyncCodeflowThrowingException>(c => c.Start()).AfterAllPendings().
                ItShouldBeSuspended();
        }

        [TestMethod]
        public void When_an_async_action_throws_an_unahandled_exception_its_dispatch_count_should_be_correct()
        {
            WhenCodeflow.AsynchronouslyExecuted<AsyncCodeflowThrowingException>(c => c.Start()).AfterAllPendings().
                CurrentAction().IsDispatched(1);
        }

        [TestMethod]
        public void When_guid_arguments_are_passed_during_jumps_they_are_correctly_dispatched()
        {
            var id = Guid.NewGuid();
            WhenCodeflow.Executed<GuidArgument>(c => c.Start(id)).AfterAllPendings().ItShouldEndWithValue(id);
        }

        [TestMethod]
        public void When_a_retry_result_is_returned_from_an_async_action_it_should_have_the_correct_dispatch_count()
        {
            WhenCodeflow.AsynchronouslyExecuted<AsyncCodeflowWithRetry>(c => c.Start(RuntimeValues.ActionContext)).
                AfterAllPendings().CurrentAction().IsDispatched(2);
        }

        [TestMethod]
        public void When_an_action_requires_a_transaction()
        {
            WhenCodeflow.Executed<TransactionalAction>(c => c.Start()).ItShouldEndWithValue(true);
        }
    }
}